package com.naiterui.ehp.bs.search.service.impl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.springframework.stereotype.Service;

import com.naiterui.common.repository.page.PageVO;
import com.naiterui.ehp.bp.bo.agent.AgentCustomBO;
import com.naiterui.ehp.bp.bo.statistics.AsConsultRecordBO;
import com.naiterui.ehp.bp.bo.statistics.AsOrderProductBO;
import com.naiterui.ehp.bp.bo.statistics.AsOrderRecordBO;
import com.naiterui.ehp.bp.bo.statistics.AsProductRecordBO;
import com.naiterui.ehp.bp.bo.statistics.AsRecipelRecordBO;
import com.naiterui.ehp.bp.support.exception.BusinessException;
import com.naiterui.ehp.bp.support.utils.JsonMapper;
import com.naiterui.ehp.bs.search.feign.IAgentFeignClient;
import com.naiterui.ehp.bs.search.service.IStatisticsService;
import com.naiterui.ehp.bs.search.service.impl.core.EsIndexService;
import com.naiterui.ehp.bs.search.service.impl.core.EsIndicesService;
import com.naiterui.ehp.bs.search.service.impl.core.EsSearchService;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@RequiredArgsConstructor
public class SearchStatisticsServiceImpl implements IStatisticsService {

    /**
     * 经纪人数据统计-问诊记录
     */
    private static final String INDEX_NAME_CONSULT_RECORD = "as_consult_record";
    /**
     * 经纪人数据统计-药品售卖记录
     */
    private static final String INDEX_NAME_PRODUCT_RECORD = "as_product_record";
    /**
     * 经纪人数据统计-订单记录
     */
    private static final String INDEX_NAME_ORDER_RECORD = "as_order_record";
    /**
     * 经纪人数据统计-处方记录
     */
    private static final String INDEX_NAME_RECIPEL_RECORD = "as_recipel_record";

    private final EsIndexService esIndexService;
    private final EsIndicesService esIndicesService;
    private final EsSearchService esSearchService;
    private final IAgentFeignClient agentFeignClient;

    @Override
    public void createConsultRecordIndex(AsConsultRecordBO consultRecordBO) {
        if (consultRecordBO == null) {
            log.info("经纪人数据统计-问诊记录为空");
            return;
        }
        AgentCustomBO agentCustomBO = this.getAgentCustom(consultRecordBO.getDoctorId());
        if (agentCustomBO == null) {
            log.info("经纪人数据统计-医生没有绑定经纪人, doctorId: {}", consultRecordBO.getDoctorId());
            return;
        }
        consultRecordBO.setAreaId(agentCustomBO.getAreaId());
        consultRecordBO.setProvinceAreaId(agentCustomBO.getProvinceAreaId());
        consultRecordBO.setAgentId(agentCustomBO.getAgentId());
        try {
            this.createIndex(INDEX_NAME_CONSULT_RECORD);
            BulkRequest bulkRequest = new BulkRequest();
            IndexRequest indexRequest = new IndexRequest(INDEX_NAME_CONSULT_RECORD);
            indexRequest.id(consultRecordBO.getConsultId().toString());
            indexRequest.source(JsonMapper.toJson(consultRecordBO), XContentType.JSON);
            indexRequest.opType("create");
            bulkRequest.add(indexRequest);
            BulkResponse bulkResponse = esIndexService.bulkOperation(INDEX_NAME_CONSULT_RECORD, bulkRequest);
            if (bulkResponse.hasFailures()) {
                log.error("经纪人数据统计-问诊记录索引插入数据失败！");
            }
        } catch (BusinessException e) {
            log.warn("经纪人数据统计-问诊记录索引插入数据异常！indexName:{}", INDEX_NAME_CONSULT_RECORD, e);
        }
    }

    @Override
    public List<AsConsultRecordBO> findConsultRecordIndex(String yesterday) throws BusinessException {
        SearchHits shs = this.getSearchHits(INDEX_NAME_CONSULT_RECORD, yesterday);
        List<AsConsultRecordBO> consultRecordList = new ArrayList<>();
        AsConsultRecordBO doctorConsultRecordBO;
        for (SearchHit sh : shs) {
            doctorConsultRecordBO = new AsConsultRecordBO();
            BeanUtil.fillBeanWithMap(sh.getSourceAsMap(), doctorConsultRecordBO, false);
            consultRecordList.add(doctorConsultRecordBO);
        }
        log.info("consult: {}", JSONUtil.toJsonStr(consultRecordList));
        return consultRecordList;
    }

    @Override
    public void createProductRecordIndex(AsProductRecordBO productRecordBO) {
        if (productRecordBO == null) {
            log.info("经纪人数据统计-药品售卖记录为空");
            return;
        }
        AgentCustomBO agentCustomBO = this.getAgentCustom(productRecordBO.getDoctorId());
        if (agentCustomBO == null) {
            log.info("经纪人数据统计-医生没有绑定经纪人, doctorId: {}", productRecordBO.getDoctorId());
            return;
        }
        productRecordBO.setAreaId(agentCustomBO.getAreaId());
        productRecordBO.setProvinceAreaId(agentCustomBO.getProvinceAreaId());
        productRecordBO.setAgentId(agentCustomBO.getAgentId());
        try {
            this.createIndex(INDEX_NAME_PRODUCT_RECORD);
            BulkRequest bulkRequest = new BulkRequest();
            IndexRequest indexRequest = new IndexRequest(INDEX_NAME_PRODUCT_RECORD);
            indexRequest.source(JsonMapper.toJson(productRecordBO), XContentType.JSON);
            indexRequest.opType("create");
            bulkRequest.add(indexRequest);
            BulkResponse bulkResponse = esIndexService.bulkOperation(INDEX_NAME_PRODUCT_RECORD, bulkRequest);
            if (bulkResponse.hasFailures()) {
                log.error("经纪人数据统计-药品售卖记录索引插入数据失败！");
            }
        } catch (BusinessException e) {
            log.warn("经纪人数据统计-药品售卖记录索引插入数据异常！indexName:{}", INDEX_NAME_PRODUCT_RECORD, e);
        }
    }

    @Override
    public List<AsProductRecordBO> findProductRecordIndex(String yesterday) throws BusinessException {
        SearchHits shs = this.getSearchHits(INDEX_NAME_PRODUCT_RECORD, yesterday);
        List<AsProductRecordBO> productRecordList = new ArrayList<>();
        AsProductRecordBO productRecordBO;
        for (SearchHit sh : shs) {
            productRecordBO = new AsProductRecordBO();
            BeanUtil.fillBeanWithMap(sh.getSourceAsMap(), productRecordBO, false);
            productRecordList.add(productRecordBO);
        }
        log.info("product: {}", JSONUtil.toJsonStr(productRecordList));
        return productRecordList;
    }

    @Override
    public void createOrderRecordIndex(AsOrderRecordBO orderRecordBO) {
        if (orderRecordBO == null) {
            log.info("经纪人数据统计-订单记录为空");
            return;
        }
        AgentCustomBO agentCustomBO = this.agentFeignClient.getAgentCustomByDoctorId(orderRecordBO.getDoctorId());
        if (agentCustomBO == null) {
            log.info("经纪人数据统计-医生没有绑定经纪人, doctorId: {}", orderRecordBO.getDoctorId());
            return;
        }
        orderRecordBO.setAreaId(agentCustomBO.getAreaId());
        orderRecordBO.setProvinceAreaId(agentCustomBO.getProvinceAreaId());
        orderRecordBO.setAgentId(agentCustomBO.getAgentId());
        try {
            this.createIndex(INDEX_NAME_ORDER_RECORD);
            BulkRequest bulkRequest = new BulkRequest();
            IndexRequest indexRequest = new IndexRequest(INDEX_NAME_ORDER_RECORD);
            indexRequest.id(orderRecordBO.getOrderSn());
            indexRequest.source(JsonMapper.toJson(orderRecordBO), XContentType.JSON);
            indexRequest.opType("create");
            bulkRequest.add(indexRequest);
            List<AsProductRecordBO> productRecordList = new ArrayList<>();
            AsProductRecordBO productRecordBO;
            for (AsOrderProductBO productBO : orderRecordBO.getOrderProductList()) {
                productRecordBO = new AsProductRecordBO();
                productRecordBO.setAreaId(orderRecordBO.getAreaId());
                productRecordBO.setAgentId(orderRecordBO.getAgentId());
                productRecordBO.setDoctorId(orderRecordBO.getDoctorId());
                productRecordBO.setPatientId(orderRecordBO.getPatientId());
                productRecordBO.setRecipelId(orderRecordBO.getRecipelId());
                productRecordBO.setProductId(productBO.getProductId());
                productRecordBO.setProductNumber(productBO.getProductNumber());
                productRecordBO.setProductPrice(productBO.getProductPrice());
                productRecordBO.setGenerateDate(orderRecordBO.getGenerateDate());
                productRecordBO.setCreatedAt(orderRecordBO.getCreatedAt());
                productRecordBO.setCreatedBy(orderRecordBO.getCreatedBy());
                productRecordList.add(productRecordBO);
            }
            BulkResponse bulkResponse = esIndexService.bulkOperation(INDEX_NAME_ORDER_RECORD, bulkRequest);
            if (bulkResponse.hasFailures()) {
                log.error("经纪人数据统计-订单记录索引插入数据失败！");
            }
            // 更新订单状态
            this.updateRecipelRecordIndex(orderRecordBO.getRecipelId());
            // 新增商品
            for (AsProductRecordBO doctorProductRecordBO : productRecordList) {
                this.createProductRecordIndex(doctorProductRecordBO);
            }
        } catch (BusinessException e) {
            log.warn("经纪人数据统计-订单记录索引插入数据异常！indexName:{}", INDEX_NAME_ORDER_RECORD, e);
        }
    }

    @Override
    public List<AsOrderRecordBO> findOrderRecordIndex(String yesterday) throws BusinessException {
        SearchHits shs = this.getSearchHits(INDEX_NAME_ORDER_RECORD, yesterday);
        List<AsOrderRecordBO> orderRecordList = new ArrayList<>();
        AsOrderRecordBO doctorOrderRecordBO;
        for (SearchHit sh : shs) {
            doctorOrderRecordBO = new AsOrderRecordBO();
            BeanUtil.fillBeanWithMap(sh.getSourceAsMap(), doctorOrderRecordBO, false);
            orderRecordList.add(doctorOrderRecordBO);
        }
        log.info("order: {}", JSONUtil.toJsonStr(orderRecordList));
        return orderRecordList;
    }

    @Override
    public void createRecipelRecordIndex(AsRecipelRecordBO recipelRecordBO) {
        if (recipelRecordBO == null) {
            log.info("经纪人数据统计-处方记录为空");
            return;
        }
        AgentCustomBO agentCustomBO = this.getAgentCustom(recipelRecordBO.getDoctorId());
        if (agentCustomBO == null) {
            log.info("经纪人数据统计-医生没有绑定经纪人, doctorId: {}", recipelRecordBO.getDoctorId());
            return;
        }
        recipelRecordBO.setAreaId(agentCustomBO.getAreaId());
        recipelRecordBO.setProvinceAreaId(agentCustomBO.getProvinceAreaId());
        recipelRecordBO.setAgentId(agentCustomBO.getAgentId());
        try {
            this.createIndex(INDEX_NAME_RECIPEL_RECORD);
            BulkRequest bulkRequest = new BulkRequest();
            IndexRequest indexRequest = new IndexRequest(INDEX_NAME_RECIPEL_RECORD);
            indexRequest.id(recipelRecordBO.getRecipelId().toString());
            indexRequest.source(JsonMapper.toJson(recipelRecordBO), XContentType.JSON);
            indexRequest.opType("create");
            bulkRequest.add(indexRequest);
            BulkResponse bulkResponse = esIndexService.bulkOperation(INDEX_NAME_RECIPEL_RECORD, bulkRequest);
            if (bulkResponse.hasFailures()) {
                log.error("经纪人数据统计-处方记录索引插入数据失败！");
            }
        } catch (BusinessException e) {
            log.warn("经纪人数据统计-处方记录索引插入数据异常！indexName:{}", INDEX_NAME_RECIPEL_RECORD, e);
        }
    }

    @Override
    public List<AsRecipelRecordBO> findRecipelRecordIndex(String yesterday) throws BusinessException {
        SearchHits shs = this.getSearchHits(INDEX_NAME_RECIPEL_RECORD, yesterday);
        List<AsRecipelRecordBO> recipelRecordList = new ArrayList<>();
        AsRecipelRecordBO recipelRecordBO;
        for (SearchHit sh : shs) {
            recipelRecordBO = new AsRecipelRecordBO();
            BeanUtil.fillBeanWithMap(sh.getSourceAsMap(), recipelRecordBO, false);
            recipelRecordList.add(recipelRecordBO);
        }
        log.info("recipel: {}", JSONUtil.toJsonStr(recipelRecordList));
        return recipelRecordList;
    }

    @Override
    public void updateRecipelRecordIndex(Long recipelId) throws BusinessException {
        UpdateRequest updateRequest = new UpdateRequest();
        updateRequest.index(INDEX_NAME_RECIPEL_RECORD);
        updateRequest.id(recipelId.toString());
        updateRequest.opType();
        try {
            updateRequest.doc(XContentFactory.jsonBuilder().startObject().field("recipelStatus", 1).endObject());
        } catch (IOException e) {
            log.error("经纪人数据统计-处方记录索引修改数据失败！:{}", e.getMessage());
        }
        esIndexService.update(updateRequest);
    }

    @Override
    public PageVO<AsOrderRecordBO> findOrderPage(Long doctorId, String orderSn, int pageNo, int pageSize)
        throws BusinessException {
        // 构造查询请求
        SearchRequest searchRequest = new SearchRequest(INDEX_NAME_ORDER_RECORD);
        SearchSourceBuilder searchSourceBuilder = SearchSourceBuilder.searchSource();

        // 设置查询条件和分页参数
        int start = (pageNo - 1) * pageSize;
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.termQuery("doctorId", doctorId));
        if (StrUtil.isNotBlank(orderSn)) {
            boolQueryBuilder.must(QueryBuilders.wildcardQuery("orderSn", "*" + orderSn + "*"));
        }
        RangeQueryBuilder rangeQueryBuilder = new RangeQueryBuilder("createdAt");
        boolQueryBuilder.must(rangeQueryBuilder.lt(new Date()));
        searchSourceBuilder.query(boolQueryBuilder);
        searchSourceBuilder.from(start);
        searchSourceBuilder.size(pageSize);
        searchSourceBuilder.sort(SortBuilders.scoreSort());
        searchRequest.source(searchSourceBuilder);
        this.createIndex(INDEX_NAME_ORDER_RECORD);
        SearchResponse response = esSearchService.search(searchRequest);
        SearchHits shs = response.getHits();
        List<AsOrderRecordBO> orderRecordList = new ArrayList<>();
        AsOrderRecordBO doctorOrderRecordBO;
        for (SearchHit sh : shs) {
            doctorOrderRecordBO = new AsOrderRecordBO();
            BeanUtil.fillBeanWithMap(sh.getSourceAsMap(), doctorOrderRecordBO, false);
            orderRecordList.add(doctorOrderRecordBO);
        }
        return PageVO.newBuilder().pageNo(pageNo).pageSize(pageSize).totalCount((int)shs.getTotalHits().value)
            .result(orderRecordList).build();
    }

    private void createIndex(String indexName) throws BusinessException {
        // 判断索引是否存在，如果不存在则创建索引
        boolean indexExists = esIndicesService.indexExists(indexName);
        if (!indexExists) {
            esIndicesService.createIndex(indexName);
        }
    }

    private SearchHits getSearchHits(String indexName, String yesterday) throws BusinessException {
        this.createIndex(indexName);
        // 构造查询请求
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.termQuery("generateDate", yesterday));
        SearchRequest searchRequest = new SearchRequest(indexName);
        searchRequest.source(searchSourceBuilder);
        log.info("statistics search query: {}", searchRequest.toString());
        SearchResponse response = esSearchService.search(searchRequest);
        return response.getHits();
    }

    private AgentCustomBO getAgentCustom(Long doctorId) {
        return this.agentFeignClient.getAgentCustomByDoctorId(doctorId);
    }

}
